package com.i72.freeway;

import com.i72.basic.SOAServiceCenter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author jiangj
 * @version 1.0.0
 * @ClassName ProxyServiceProcessor.java
 * @Description TODO
 * @createTime 2021年12月28日 14:08:00
 */
@Slf4j
public class ProxyServiceProcessor implements ApplicationContextAware,InitializingBean, BeanDefinitionRegistryPostProcessor, InstantiationAwareBeanPostProcessor {

    private static final Map<Class, List<ProxyModel>> registerBeans         = new HashMap<>();
    private static final Set<Class> alreadyRegisteredBean = new HashSet<>();

    private Set<Class>               allFreewayList = new LinkedHashSet<>();
    //private FreewayVersionProperties freewayVersionProperties;

    @Value("${za.freeway.scanPackge}")
    private String packages;

    private ApplicationContext applicationContext = null;


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }



    @Override
    @SuppressWarnings("unchecked")
    public void afterPropertiesSet() {
        // 1、扫描有@FreewayPath的classes
        TypeFilter typeFilter = (metadataReader, metadataReaderFactory) -> metadataReader.getAnnotationMetadata()
                .getAnnotationTypes()
                .contains(FreewayPath.class.getName());

        //List<String> scanFreewayPaths = SwjConfig.getPropertiesValueList("swj.freeway.scan-freeway-path");

        // 如有配置扫描包路径，扫描classpath*:/{scanFreewayPath}/**/*.class



        List<String> packageList = new ArrayList<>();
        if(!StringUtils.isEmpty(packages)){
            if(packages.contains(",")){
                String [] ps = packages.split(",");
                if(ps!=null && ps.length>0){
                    packageList.addAll(Arrays.stream(ps).collect(Collectors.toList()));
                }
            }else{
                packageList.add(packages);
            }
        }else{
            String beanName = AutoConfigurationPackages.class.getName();

            Object o = applicationContext.getBean(beanName);

            try {
                Field f = o.getClass().getDeclaredField("packages");
                f.setAccessible(true);
                List<String> ps = (List<String>) f.get(o);
                packageList.addAll(ps);
            }catch (Exception e){

            }

            String v = "";
        }

        for (String scanBasePackage : packageList) {
            //allFreewayList.addAll(SpringResourceHelper.scanPackage(FreewayPath.class,scanBasePackage));
            allFreewayList.addAll(SpringResourceHelper.getClasses(scanBasePackage, new TypeFilter[] { typeFilter }, null));
        }

        /*
        // 2、调用接口版本解析
        try (InputStream inputStream = SpringResourceHelper.getFile("classpath:application.yml")) {
            Iterable<Object> iterable = new Yaml(new Constructor()).loadAll(inputStream);
            if (iterable.iterator().hasNext()) {
                Map<String, Object> yml = ((Map<String, Object>) iterable.iterator().next());
                if (MapUtils.isNotEmpty(yml)) {
                    Map<String, Object> swj = (Map<String, Object>) yml.get("swj");
                    if (MapUtils.isNotEmpty(swj)) {
                        Map<String, Object> freeway = (Map<String, Object>) swj.get("freeway");
                        String json = JsonHelper.toJsonString(freeway);
                        freewayVersionProperties = JsonHelper.parseBean(json, FreewayVersionProperties.class);
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }*/

        // 3、生成代理类
        for (Class api : allFreewayList) {
            try {
                if (alreadyRegisteredBean.contains(api)) {
                    continue;
                }

                // 若接口有实现类，不生成代理类
                String[] beans = applicationContext.getBeanNamesForType(api);
                if (beans.length > 0) {
                    FreewayConfig.addAppFreewayApi(api);
                    continue;
                }

                // 生成服务代理类
                List<ProxyModel> proxyModels = Proxy.getProxy(api);

                // 保存至Map，后续在postProcessBeanDefinitionRegistry构建BeanDefinition
                registerBeans.put(api, proxyModels);
                alreadyRegisteredBean.add(api);
            } catch (Exception e) {
                log.error("generate freeway proxy class failure, api: {}", api.getName());
                throw e;
            }
        }

        /*
        // @FreewayPath的接口有实现类，则表示这是服务提供者，注册服务到ZK
        if (!FreewayConfig.getAppFreewayApiList().isEmpty()
                && (SwjConfig.getIsRegisterApi() == null || Boolean.parseBoolean(SwjConfig.getIsRegisterApi()))) {
            SwjConfig.setIsRegisterApi(Boolean.TRUE.toString());
            SpringContextHelper.getBeanFactory().registerSingleton("servletRegistrationBean", ApiServlet.getServletRegistrationBean());
        }*/
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        for (Map.Entry<Class, List<ProxyModel>> entry : registerBeans.entrySet()) {
            for (ProxyModel registerBean : entry.getValue()) {
                try {
                    registry.getBeanDefinition(registerBean.getBeanName());
                } catch (NoSuchBeanDefinitionException e) {
                    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(registerBean.getClazz());
                    GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
                    definition.setBeanClass(registerBean.getProxyClass());
                    definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
                    definition.setScope(GenericBeanDefinition.SCOPE_SINGLETON);
                    registry.registerBeanDefinition(registerBean.getBeanName(), definition);
                    log.debug("proxy bean [{}:{}] injected in spring", registerBean.getBeanName(), registerBean.getProxyClass().getName());
                }
            }
        }
    }



    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
        //if ("ALL".equalsIgnoreCase(SwjConfig.get(SOAServiceCenter.SUBSCRIBE_LIST))) return null;
        // 获取需要订阅的serviceName
        try {
            // 属性注入
            Class<?> clazz = beanClass;
            while (clazz != null && Object.class != clazz) {
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                    Class<?> apiClass = field.getType();
                    if (field.getAnnotation(Autowired.class) == null && field.getAnnotation(Resource.class) == null) continue;
                    if (apiClass.getAnnotation(FreewayPath.class) == null) continue;
                    String serviceName = ClassUtils.getAppNode(apiClass);
                    if(!StringUtils.isEmpty(serviceName)){
                    //if (StringUtils.isNotBlank(serviceName)) {
                        applicationContext.getBean(SOAServiceCenter.class).addSubscribeServiceName(serviceName);
                        //SOAServiceCenter.addSubscribeServiceName(serviceName);
                    }
                }
                clazz = clazz.getSuperclass();
            }

            // 构造函数注入
            for (java.lang.reflect.Constructor constructor : beanClass.getConstructors()) {
                if (constructor.getAnnotation(Autowired.class) == null && constructor.getAnnotation(Resource.class) == null) continue;
                for (Class apiClass : constructor.getParameterTypes()) {
                    if (apiClass.getAnnotation(FreewayPath.class) == null) continue;
                    //String serviceName = ClassUtils.getAppNode(apiClass);
                    /*
                    if (StringUtils.isNotBlank(serviceName)) {
                        SOAServiceCenter.addSubscribeServiceName(serviceName);
                    }*/
                }
            }
            // setter方法注入
            BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                if (!propertyDescriptor.getName().equals("class")) {
                    Method setter = propertyDescriptor.getWriteMethod();
                    if (setter == null || (setter.getAnnotation(Autowired.class) == null && setter.getAnnotation(Resource.class) == null)) continue;
                    for (Class apiClass : setter.getParameterTypes()) {
                        if (apiClass.getAnnotation(FreewayPath.class) == null) {
                            continue;
                        }
                        //String serviceName = ClassUtils.getAppNode(apiClass);
                        /*
                        if (StringUtils.isNotBlank(serviceName)) {
                            SOAServiceCenter.addSubscribeServiceName(serviceName);
                        }*/
                    }
                }
            }
        } catch (BeansException | IntrospectionException e) {
            throw new RuntimeException("获取订阅服务节点失败", e);
        }
        return null;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}
